package ucuserslogic

import (
	"context"
	"fmt"
	"github.com/zeromicro/go-zero/core/logx"
	"go.uber.org/zap"
	"navi-mumbai/app/rpc/model/pg/usercenter"
	adminlogic "navi-mumbai/app/rpc/usercenter/internal/logic/ucadmin"
	"navi-mumbai/app/rpc/usercenter/internal/svc"
	"navi-mumbai/app/rpc/usercenter/pb"
	"navi-mumbai/common/key/dbkey"
	"navi-mumbai/common/key/idkey"
	"navi-mumbai/common/key/userkey"
	"navi-mumbai/common/key/walletkey"
	"navi-mumbai/common/utils"
	"navi-mumbai/common/xerr"
	"navi-mumbai/third/login/google"
	"strconv"
	"time"
)

var cacheMumbaiUserAccountGoogleTokenPrefix = "cache:mumbai:userAccount:googleToken"

type GoogleUserLoginLogic struct {
	ctx    context.Context
	svcCtx *svc.ServiceContext
	logx.Logger
}

func NewGoogleUserLoginLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GoogleUserLoginLogic {
	return &GoogleUserLoginLogic{
		ctx:    ctx,
		svcCtx: svcCtx,
		Logger: logx.WithContext(ctx),
	}
}

func (l *GoogleUserLoginLogic) GoogleUserLogin(in *pb.GoogleUserLoginReq) (*pb.LoginUserResp, error) {
	defer utils.RecoverPanic()

	l.Logger.Infof("GoogleUserLoginReq begin clientId %s token %s ", userkey.GoogleLoginClientId, in.GoogleToken)

	google.InitHttpClient("https://oauth2.googleapis.com/")
	req := &google.VerifyReq{
		GoogleToken: in.GoogleToken,
	}
	resp, err := google.Hc.VerifyToken(req)
	if err != nil {
		l.Logger.Errorf("err == ", zap.Error(err))
		return nil, err
	}

	if resp.Data.Aud != userkey.GoogleLoginClientId {
		l.Logger.Errorf("【Google Login】 ClientId is incorrect! return Aud %s ", resp.Data.Aud)
		return nil, xerr.NewGrpcErrCodeMsg(xerr.AuthFail, "ClientId is incorrect")
	}

	if resp.Data.Iss != userkey.GoogleLoginIssuedToDomain1 && resp.Data.Iss != userkey.GoogleLoginIssuedToDomain2 {
		l.Logger.Errorf("【Google Login】 IssuedTo is incorrect! Iss %s ", resp.Data.Iss)
		return nil, xerr.NewGrpcErrCodeMsg(xerr.AuthFail, "IssuedTo is incorrect")
	}

	// 是否注册
	userAcc, err := l.svcCtx.UserAccountModel.FindOneByAuthKeyAuthType(l.ctx, resp.Data.Sub, userkey.UserAuthTypeGoogle)
	if err != nil && err != usercenter.ErrNotFound {
		l.Logger.Errorf("err == ", zap.Error(err))
		return nil, xerr.NewGrpcErrCodeMsg(xerr.DbError, fmt.Sprintf("err:%+v", err))
	}
	if userAcc == nil { // 未注册用户
		return l.register(in, resp)
	}

	currTime := time.Now()
	expDurTime, _ := strconv.ParseInt(resp.Data.Exp, 10, 64)
	if currTime.Sub(userAcc.UpdateTime).Seconds() > float64(expDurTime) {
		l.Logger.Errorf("token is expired ! currTime %d updateTime %d expDurTime %d ", currTime.Unix(), userAcc.UpdateTime.Unix(), expDurTime)
		return nil, xerr.NewGrpcErrCodeMsg(xerr.AuthFail, "token is expired")
	}

	if userAcc.AuthKey != resp.Data.Sub {
		l.Logger.Errorf("token is invalid ! AuthKey %s Sub %s ", userAcc.AuthKey, resp.Data.Sub)
		return nil, xerr.NewGrpcErrCodeMsg(xerr.AuthFail, "token is invalid!")
	}

	rsp, err := l.doLogin(in, userAcc)
	if err != nil {
		return nil, err
	}
	// 保存历史记录
	_, err = l.svcCtx.UserVerifyLogModel.Insert(l.ctx, &usercenter.UserVerifyLog{Uid: userAcc.Uid, VerifyPass: in.GoogleToken})
	if err != nil {
		l.Logger.Errorf("err == ", zap.Error(err))
	}
	return rsp, nil
}
func (l *GoogleUserLoginLogic) doLogin(in *pb.GoogleUserLoginReq, uac *usercenter.UserAccount) (*pb.LoginUserResp, error) {
	in2 := pb.LoginUserNoAuthReq{
		UserChannel:  in.UserChannel,
		AppVer:       in.AppVer,
		Ip:           uac.RegIp,
		Uid:          uac.Uid,
		Device:       in.Device,
		DeviceId:     in.DeviceId,
		AppsFlyerUID: in.AppsFlyerUID,
	}
	// 登陆时，更新appsFlyeruid
	uac.AppsFlyerUid = in.AppsFlyerUID
	err := l.svcCtx.UserAccountModel.Update(l.ctx, uac)
	if err != nil {
		l.Logger.Errorf("err == ", zap.Error(err))
		return nil, err
	}
	return l.noAuthLogin(&in2, uac)
}

func (l *GoogleUserLoginLogic) noAuthLogin(in *pb.LoginUserNoAuthReq, userAcc *usercenter.UserAccount) (*pb.LoginUserResp, error) {
	l.Logger.Infof("noAuthLogin ...")

	// update last noAuthLogin time
	var uls *usercenter.UserLoginStat
	var err error
	uls, err = l.svcCtx.UserLoginStatModel.FindOne(l.ctx, userAcc.Uid)
	if err != nil {
		l.Logger.Errorf("err == ", zap.Error(err))
		return nil, xerr.NewGrpcErrCodeMsg(xerr.DbError, err.Error())
	}

	if time.Now().Day() != uls.LoginTime.Day() {
		uls.TodayLoginCnt = 1
		uls.LoginDays++
		// 首次登陆
		l.todayFirstLogin(userAcc.Uid)
	} else {
		uls.TodayLoginCnt++
	}
	uls.LoginTime = time.Now()
	uls.LoginCntSum++
	uls.AppVer = strconv.FormatInt(in.AppVer, 10)
	uls.LoginIp = in.Ip
	uls.LoginDevice = in.Device
	uls.LoginDeviceId = in.DeviceId

	err = l.svcCtx.UserLoginStatModel.Update(l.ctx, uls)
	if err != nil {
		l.Logger.Errorf("err == ", zap.Error(err))
		return nil, xerr.NewGrpcErrCodeMsg(xerr.DbError, err.Error())
	}

	// check auto Unban
	if userAcc.AccountState == userkey.AccountStateBan {
		userAcc.AccountState, err = l.checkUnban(userAcc.Uid, userAcc.AuthKey, userAcc.AuthType)
		if err != nil {
			l.Logger.Errorf("err == ", zap.Error(err))
			return nil, err
		}
	}

	// token
	tokenResp := &pb.GenerateTokenResp{
		AccessToken: "",
		ExpiresIn:   0,
	}

	if userAcc.AccountState == userkey.AccountStateNormal {
		generateTokenLogic := NewGenerateTokenLogic(l.ctx, l.svcCtx)
		tokenResp, err = generateTokenLogic.GenerateToken(&pb.GenerateTokenReq{
			Uid:         userAcc.Uid,
			UserChannel: userAcc.UserChannel,
			AppVer:      in.AppVer,
			AuthType:    userAcc.AuthType,
			AuthKey:     userAcc.AuthKey,
		})
		if err != nil {
			l.Logger.Errorf("Login generateTokenLogic.GenerateToken err ", zap.Error(err))
			return nil, err
		}
	}

	//
	var up *usercenter.UserProfile
	up, err = l.svcCtx.UserProfileModel.FindOne(l.ctx, userAcc.Uid)
	if err != nil && err != usercenter.ErrNotFound {
		l.Logger.Errorf("err == ", zap.Error(err))
		return nil, xerr.NewGrpcErrCodeMsg(xerr.DbError, err.Error())
	}

	var rsp pb.LoginUserResp
	rsp = pb.LoginUserResp{
		Account:       &pb.UserAccount{Uid: userAcc.Uid, AccountState: userAcc.AccountState, Username: up.Name, IsNewUser: dbkey.Disable},
		Token:         tokenResp,
		Profile:       &pb.UserProfile{},
		NotifyMsgStat: &pb.UserNotifyMsgStat{},
		AppConfig:     &pb.AppConfig{},
		BannerConfig:  &pb.BannerConfig{List: make([]*pb.BannerData, 0)},
		RewardStat:    &pb.UserRewardStat{},
	}
	if up != nil {
		rsp.Profile.Avatar = up.Avatar
		rsp.Profile.Language = up.Language
		rsp.Profile.Email = up.Email
		rsp.Profile.AreaNo = up.AreaNo
		rsp.Profile.PhoneNo = up.PhoneNo
	}

	//
	var unms *usercenter.UserNotifyMsgStat
	unms, err = l.svcCtx.UserNotifyMsgStatModel.FindOne(l.ctx, userAcc.Uid)
	if err != nil && err != usercenter.ErrNotFound {
		l.Logger.Errorf("err == ", zap.Error(err))
		return nil, xerr.NewGrpcErrCodeMsg(xerr.DbError, err.Error())
	}
	if unms != nil {
		rsp.NotifyMsgStat.NotRead = unms.NotRead
		rsp.NotifyMsgStat.Total = unms.Total
	}

	// app config
	var appConfig *usercenter.AppConfig
	appConfig, err = l.svcCtx.AppConfigModel.FindOne(l.ctx, in.AppVer)
	if err != nil && err != usercenter.ErrNotFound {
		l.Logger.Errorf("err == ", zap.Error(err))
		return nil, xerr.NewGrpcErrCodeMsg(xerr.DbError, err.Error())
	}
	if appConfig != nil {
		if in.AppVer == appConfig.AppVersion {
			rsp.AppConfig.PayState = appConfig.PayState
			rsp.AppConfig.TextState = appConfig.TextState
			rsp.AppConfig.RankState = appConfig.RankState
		}
	}

	// banner config
	var bannerConfig []*usercenter.BannerConfig
	bannerConfig, err = l.svcCtx.BannerConfigModel.Find(l.ctx, userkey.BannerStateOpen, 1, 20)
	if len(bannerConfig) > 0 {
		for i := 0; i < len(bannerConfig); i++ {
			rsp.BannerConfig.List = append(rsp.BannerConfig.List, &pb.BannerData{
				Id:       bannerConfig[i].Id,
				Image:    bannerConfig[i].BannerImage,
				Url:      bannerConfig[i].Url,
				PagePath: bannerConfig[i].PagePath,
				Order:    bannerConfig[i].BannerOrder,
			})
		}
	}
	// reward stat
	var ugs *usercenter.UserRewardStat
	ugs, err = l.svcCtx.UserRewardStatModel.FindOne(l.ctx, userAcc.Uid)
	if err != nil && err != usercenter.ErrNotFound {
		l.Logger.Errorf("err == ", zap.Error(err))
		return nil, xerr.NewGrpcErrCodeMsg(xerr.DbError, err.Error())
	}
	if ugs != nil {
		rsp.RewardStat.Total = ugs.Total
		rsp.RewardStat.NotReceived = ugs.NotReceived
	}

	// login log
	addLoginLog(l.ctx, l.svcCtx, userAcc.Uid, userAcc.UserChannel)
	return &rsp, nil
}

// 自动解封
func (l *GoogleUserLoginLogic) checkUnban(uid int64, authKey string, authType int64) (state int64, err error) {
	var ub *usercenter.UserBan
	ub, err = l.svcCtx.UserBanModel.FindOne(l.ctx, uid)
	if err != nil && err != usercenter.ErrNotFound {
		l.Logger.Errorf("err == ", zap.Error(err))
		return 0, xerr.NewGrpcErrCodeMsg(xerr.DbError, err.Error())
	}

	if ub == nil {
		l.Logger.Errorf("user banner not exist ")
		return userkey.AccountStateNormal, nil
	}

	if ub.AutoUnbanTime.Before(time.Now()) { // 自动解封
		lu := adminlogic.NewUnbanUserLogic(l.ctx, l.svcCtx)
		req := pb.UnbanUserReq{
			Uid:      uid,
			AdminUid: idkey.SystemUid,
		}
		_, err = lu.DoUnbanUser(&req, authKey, authType)
		return userkey.AccountStateNormal, err
	}

	return userkey.AccountStateBan, nil
}

func (l *GoogleUserLoginLogic) register(in *pb.GoogleUserLoginReq, resp *google.VerifyResultResp) (*pb.LoginUserResp, error) {
	return NewRegisterLogic(l.ctx, l.svcCtx).doGoogleUserLoginRegister(in, resp)
}

// 今日首次登录
func (l *GoogleUserLoginLogic) todayFirstLogin(uid int64) {
	unm := usercenter.UserReward{
		Uid:          uid,
		RewardType:   walletkey.UsingCurrencyType, //userkey.RewardTypeGold,
		RewardAmount: userkey.RegisterRewardMoneyAmount,
		ActivityId:   userkey.ActivityIdLogin,
		State:        userkey.RewardStateCreated,
	}

	// 登陆送金币
	_, err := l.svcCtx.UserRewardModel.Insert(l.ctx, &unm)
	if err != nil {
		l.Logger.Errorf("err == ", zap.Error(err))
		return
	}
}
